-- $LastChangedDate: 2007-06-01 02:52:33 +0200 (Fri, 01 Jun 2007) $
-- $LastChangedRevision: 149 $
-- $LastChangedBy: vicente $

-- packages de ada
WITH Ada.Numerics.Float_Random;
USE Ada.Numerics.Float_Random;
WITH Ada.Text_Io;
USE Ada.Text_Io;

-- packages del gimnasio
WITH Listas.Aparatos_Lists;
USE Listas.Aparatos_Lists;
WITH Tareas.Gmonitores;
USE Tareas.Gmonitores;
WITH Tareas.Gmonitores;
WITH Tiposprotegidos.Log;
USE Tiposprotegidos.Log;

PACKAGE BODY Tareas.Gaparatos IS

   PROCEDURE Realizar_Ejercicio (
         Ej         : IN     Ejerciciotype;
         T          : IN     Duration;
         Cliente    : IN     Datosclienteptr;
         G          : IN     Generator;
         Medicion   :    OUT Medicionbiometrica;
         Emergencia :    OUT Boolean) IS
   SEPARATE;

   TASK BODY Aparato IS
      Emergencia : Boolean;
      Medicion   : Medicionbiometrica;
      Mon        : Monitorptr;
      Salir      : Boolean            := False;
      -- generador U(0,1)
      G : Generator;
   BEGIN
      Print("aparato: start");

      -- El aparato se da de alta en el gestor de aparatos que obtuvo por
      -- parámetro
      Ga.Add_Aparato(Current_Task, Aparato'Unchecked_Access);

      -- inicializar el generador de numeros aleatorios
      Reset (G);

      WHILE NOT Salir LOOP
         SELECT
            ACCEPT Entrenar (
                  Ej          : IN     Ejerciciotype;
                  T           : IN     Duration;
                  Cliente     :        Datosclienteptr;
                  Vete_A_Casa :    OUT Boolean) DO

               Print("aparato: he sido programado para realizar el " & Ej & " durante"
                  & T'Img & " unidades de tiempo");

               Realizar_Ejercicio(Ej, T, Cliente, G, Medicion, Emergencia);

               -- Enviar al servicio de seguimiento los valores obtenidos en Medicion
               Ss.Addmedicion(Entrenar'Caller, Medicion);

               IF Emergencia = True THEN
                  -- actualizar log
                  Log_Add(Cliente_Emergencia);
                  Gm.Monitorpetitionemergencia(Mon);
                  -- requeue hacia el monitor obtenido del cliente que invoco al aparato
                  REQUEUE Mon.Atender_Enfermo;
               END IF;
               Vete_A_Casa:= False;
            END Entrenar;
         OR
            ACCEPT Acabar DO
               Salir:= True;
               -- log
               Log_Dec(Aparato_Libre);
               Log_Dec(Aparato_Cantidad);
               Print_Term("aparato: Aparato desconectado");
            END Acabar;
         OR
            TERMINATE;
         END SELECT;
         IF NOT Salir THEN
            Ga.Aparatofree(Current_Task);
         END IF;
      END LOOP;
   EXCEPTION
      WHEN Ocurrencia : OTHERS =>
         Put_Line ("Aparato: "&Ada.Exceptions.Exception_Information (Ocurrencia));
   END Aparato;

   -- Busca en la lista de aparatos un aparato disponible
   PROCEDURE Get_Free_Aparato (
         L : IN     Listas.Aparatos_Lists.List_Type;
         E : IN     Ejerciciotype;
         A :    OUT Aparatoptr) IS
      Encontrado : Boolean                             := False;
      It         : Listas.Aparatos_Lists.List_Iterator;
      Nodo       : Nodoaparato;
   BEGIN
      It := Listas.Aparatos_Lists.First(L);
      WHILE It /= Listas.Aparatos_Lists.Last(L) AND NOT Encontrado LOOP
         Nodo := Listas.Aparatos_Lists.Value(It);
         IF Nodo.Estado = Libre THEN
            Encontrado:= True;
            A := Nodo.Aptr;
            Nodo.Estado := Ocupado;
            Listas.Aparatos_Lists.Update(It, Nodo);
         ELSE
            It := Listas.Aparatos_Lists.Succ(It);
         END IF;
      END LOOP;

      IF NOT Encontrado THEN
         A := NULL;
         Print("get_free_aparato: No se ha encontrado un aparato libre " &
            "para el ejercicio " & E);
      END IF;
   END Get_Free_Aparato;

   PROCEDURE Append_Aparato (
         Alist  : Listas.Aparatos_Lists.List_Type;
         Aparid : Task_Id;
         A      : Aparatoptr) IS
      It : Listas.Aparatos_Lists.List_Iterator;
   BEGIN
      It := Listas.Aparatos_Lists.Last(Alist);
      Listas.Aparatos_Lists.Insert(It, (Aparid, A, Libre));

      Print("append_aparato: añadido aparato con Id " & Image(Aparid));
   END Append_Aparato;

   PROCEDURE Mark_Aparato_As_Idle (
         Alist  : Listas.Aparatos_Lists.List_Type;
         Aparid : Task_Id) IS
      Encontrado : Boolean                             := False;
      It         : Listas.Aparatos_Lists.List_Iterator;
      Nodo       : Nodoaparato;
   BEGIN
      -- busqueda
      It := Listas.Aparatos_Lists.First(Alist);
      WHILE It /= Listas.Aparatos_Lists.Last(Alist) AND NOT Encontrado LOOP
         Nodo := Listas.Aparatos_Lists.Value(It);
         IF Nodo.Id = Aparid THEN
            Encontrado:= True;
         ELSE
            It := Listas.Aparatos_Lists.Succ(It);
         END IF;
      END LOOP;

      -- acciones
      IF NOT Encontrado THEN
         Print("Mark_aparato_as_idle: el aparato " & Image(Aparid) &
            " no existe");
      ELSE
         Nodo.Estado := Libre;
         Listas.Aparatos_Lists.Update(It, Nodo);
      END IF;
   END Mark_Aparato_As_Idle;

   PROCEDURE End_Aparatos (
         L : IN     Listas.Aparatos_Lists.List_Type) IS
      It   : Listas.Aparatos_Lists.List_Iterator;
      Nodo : Nodoaparato;
   BEGIN
      It := Listas.Aparatos_Lists.First(L);
      WHILE It /= Listas.Aparatos_Lists.Last(L) LOOP
         Nodo := Listas.Aparatos_Lists.Value(It);
         -- apagar el aparato
         Nodo.Aptr.Acabar;
         --siguiente
         It := Listas.Aparatos_Lists.Succ(It);
      END LOOP;
   END End_Aparatos;

   TASK BODY Gaparatos IS
      Alist         : Listas.Aparatos_Lists.List_Type;
      Free_Aparatos : Natural                         := 0;
      Salir         : Boolean                         := False;
   BEGIN
      Print("gaparatos: start");
      WHILE NOT Salir LOOP
         SELECT
            ACCEPT Add_Aparato (
                  Aparid : IN     Task_Id;
                  A      : IN     Aparatoptr) DO
               Append_Aparato(Alist, Aparid, A);
               Free_Aparatos := Free_Aparatos + 1;
               Print(Integer'Image(Free_Aparatos) & " aparatos libres");
               -- log
               Log_Add(Aparato_Cantidad);
               Log_Add(Aparato_Libre);
            END Add_Aparato;
         OR
            WHEN Free_Aparatos > 0 =>
            ACCEPT Get_Aparato (
                  A     :    OUT Aparatoptr;
                  Ejerc : IN     Ejerciciotype) DO
               Get_Free_Aparato(Alist, Ejerc, A);
               Free_Aparatos := Free_Aparatos - 1;
               Print(Integer'Image(Free_Aparatos) & " aparatos libres");
               -- log
               Log_Dec(Aparato_Libre);
            END Get_Aparato;
         OR
            ACCEPT Aparatofree (
                  Aparid : IN     Task_Id) DO
               Mark_Aparato_As_Idle(Alist, Aparid);
               Free_Aparatos := Free_Aparatos + 1;
               Print(Integer'Image(Free_Aparatos) & " aparatos libres");
               -- log
               Log_Add(Aparato_Libre);
            END Aparatofree;
         OR
            ACCEPT Acabar DO
               Salir:=True;
               End_Aparatos(Alist);
               Print_Term("gestor de aparatos: Van a cerrar el gimnasio");
            END Acabar;
         OR
            TERMINATE;
         END SELECT;
      END LOOP;
   EXCEPTION
      WHEN Ocurrencia : OTHERS =>
         Put_Line ("Gestor de Aparatos: "&Ada.Exceptions.Exception_Information (Ocurrencia));
   END Gaparatos;

END Tareas.Gaparatos;
